package nicg.st.service.ahp;

/**
 * @author GeChen
 * @Description
 * @date 2019/11/3 23:04
 */
public class MatrixLab {
    private static int N;
    private double eps;
    private double[][] A;
    private double[] U;
    private double[] V;
    private int[] Z;
    //计算1000次平的均随机一致性指标
    public static double[] RI = {0, 0, 0.52, 0.89, 1.12, 1.26, 1.36, 1.41, 1.46, 1.49};

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="n">矩阵的维长度</param>
    /// <param name="e">求解精度</param>
    /// <param name="a">矩阵对象</param>
    public MatrixLab(int n, double e, double[][] a) {
        N = n;
        eps = e;
        A = new double[N][N];
        U = new double[N];
        V = new double[N];
        buildMatrix(a);
    }

    /// <summary>
    /// 构造一个方阵
    /// </summary>
    /// <param name="a">输入该方阵</param>
    private void buildMatrix(double[][] a) {
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                A[i][j] = a[i][j];
            }
        }
    }

    /// <summary>
    /// 求一个一维数组中的最大值
    /// </summary>
    /// <param name="W">一个一维数组</param>
    /// <returns>最大值</returns>
    private double maxValue(double[] W) {
        double tmp = 0;
        if (W.length != 0)
            tmp = W[0];
        for (int i = 1; i < W.length; i++) {
            if (W[i] > tmp)
                tmp = W[0];
        }
        return tmp;
    }

    /// <summary>
    /// 使用幂法求一个方阵的最大特征值和它所对应的特征向量
    /// </summary>
    /// <param name="W">最大特征值所对应的特征向量</param>
    /// <param name="max">最大特征值</param>
    /// /// <param name="round">结果小数点后的尾数长度（精度）</param>
    //求最大特征向量
    public double[] returnResult1(double[] W, double max) {
        int i = 0;
        for (i = 0; i < N; i++) {
            U[i] = 1;
        }
        int k = 1;
        double m0, mk = 0;
        m0 = maxValue(U);

        for (i = 0; i < N; i++) {
            U[i] = 1;
        }
        m0 = maxValue(U);

        while (true) {
            for (i = 0; i < N; i++) {
                V[i] = U[i] / m0;
            }
            for (i = 0; i < N; i++) {
                double sum = 0;
                for (int j = 0; j < N; j++) {
                    sum += A[i][j] * V[j];
                }
                U[i] = sum;
            }
            mk = maxValue(U);
            if (Math.abs(mk - m0) >= eps) {
                if ((Math.abs(mk - m0) / (1 + mk)) >= eps) {
                    m0 = mk;
                    k++;
                } else
                    break;
            } else
                break;
        }
        if (Math.abs(mk - m0) < eps || (Math.abs(mk - m0) >= eps && (Math.abs(mk - m0) / (1 + mk)) < eps)) {
            W = V;
            max = mk;
        }

        double SUM = 0;
        for (i = 0; i < W.length; i++) {
            SUM += W[i];
        }
        for (i = 0; i < W.length; i++) {
            W[i] = W[i] / SUM;
        }
        return W;
    }

    //求最大特征值
    public double returnResult2(double[] W, double max) {
        int i = 0;
        for (i = 0; i < N; i++) {
            U[i] = 1;
        }
        int k = 1;
        double m0, mk = 0;
        m0 = maxValue(U);

        for (i = 0; i < N; i++) {
            U[i] = 1;
        }
        m0 = maxValue(U);

        while (true) {
            for (i = 0; i < N; i++) {
                V[i] = U[i] / m0;
            }
            for (i = 0; i < N; i++) {
                double sum = 0;
                for (int j = 0; j < N; j++) {
                    sum += A[i][j] * V[j];
                }
                U[i] = sum;
            }
            mk = maxValue(U);
            if (Math.abs(mk - m0) >= eps) {
                if ((Math.abs(mk - m0) / (1 + mk)) >= eps) {
                    m0 = mk;
                    k++;
                } else
                    break;
            } else
                break;
        }
        if (Math.abs(mk - m0) < eps || (Math.abs(mk - m0) >= eps && (Math.abs(mk - m0) / (1 + mk)) < eps)) {
            W = V;
            max = mk;
        }

        double SUM = 0;
        for (i = 0; i < W.length; i++) {
            SUM += W[i];
        }
        for (i = 0; i < W.length; i++) {
            W[i] = W[i] / SUM;
        }
        return max;
    }

    /// <summary>
    /// 计算一致性指标CI
    /// </summary>
    /// <param name="lamda">n阶矩阵的最大特征值</param>
    /// <param name="n">矩阵的阶数</param>
    /// <returns>一致性指标CI的值</returns>
    public static double CI(double lamda, int n) {
        if (n > 1 && lamda >= n)
            return (lamda - n) / (n - 1);
        else
            return -1;
    }
}